package com.fzd.code.ldap;

import com.fzd.code.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

@Component
@Slf4j
public class LdapUtil {
	private LdapConfig ldapConfig;

	public void setLdapConfig(LdapConfig ldapConfig) {
		this.ldapConfig = ldapConfig;
	}

	public void login(Account account) throws Exception {
		if(ldapConfig.getIsSsl().equals("true")){
			Result.failedThrow(
					ldapLoginSSL(account),"账户或密码错误"
			);
		}else{
			Result.failedThrow(
					ldapLoginNormal(account),"账户或密码错误"
			);
		}
	}
	
	private boolean ldapLoginNormal(Account account) throws Exception {
		boolean retVal = false;
		
		//生成URL
		String url = "ldap://"+ ldapConfig.getAddress()+":"+ ldapConfig.getPortNormal();
		String username=String.format("%s@"+this.ldapConfig.getDomain(),account.getAccountId());

		//注册环境变量
		Hashtable<String, String> env = new Hashtable<>();
		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
		env.put(Context.PROVIDER_URL, url);		
		env.put(Context.SECURITY_PRINCIPAL, username);
		env.put(Context.SECURITY_CREDENTIALS, account.getPassword());
		
		log.info("============================= ldap url:"+url);
		log.info("============================= ldap principal:"+username);
		log.info("============================= ldap password:"+account.getPassword());

		return login(env,account);
	}
	
	private boolean ldapLoginSSL(Account account) throws Exception {
		log.info(" ============================ do ldap login via ssl");

		//生成URL
		String url = "ldaps://"+ ldapConfig.getAddress()+":"+ ldapConfig.getPortSsl();
		String username=String.format("%s@"+this.ldapConfig.getDomain(),account.getAccountId());
		
		//注册环境变量
		Hashtable<String, String> env = new Hashtable<String, String>();
		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
		env.put(Context.PROVIDER_URL, url);		
		env.put(Context.SECURITY_PRINCIPAL, username);
		env.put(Context.SECURITY_CREDENTIALS, account.getPassword());
		env.put(Context.SECURITY_PROTOCOL, "ssl");
		env.put("java.naming.ldap.factory.socket", "com.fzd.sms.system.util.DummySSLSocketFactory");
		
		log.info("============================= ldap url:"+url);
		log.info("============================= ldap principal:"+username);
		log.info("============================= ldap password:"+account.getPassword());

		return login(env,account);
	}

	private boolean login(Hashtable<String, String> env,Account account) throws Exception{
		LdapContext ctx = null;
		try {
			Control[] controls=null;
			ctx = new InitialLdapContext(env,controls);
			Map<String,String> userInfo=getUser(ctx,account.getAccountId());
			log.info("ldap user info:{}",userInfo);
			account.setUserName(userInfo.get("displayName"));
			account.setDepartment(userInfo.get("department"));
			return true;
		}finally{
			if (ctx != null) {
				ctx.close();
			}
		}
	}

	public Map<String, String> getUser(LdapContext ctx, String username) throws Exception{
		Map<String, String> returnMap = new HashMap<>();
		SearchControls constraints = new SearchControls();
		constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
		String filter = "(&(objectClass=organizationalPerson)(sAMAccountName=" + username + "))";
		NamingEnumeration en = ctx.search("OU=江苏省镇江地方税务局,DC=zjds,DC=cn", filter, constraints);
		while (en != null && en.hasMoreElements()) {
			Object obj = en.nextElement();
			if (! (obj instanceof SearchResult)) continue;
			SearchResult si = (SearchResult) obj;
			Attributes attrs = si.getAttributes();
			if (attrs == null) continue;
			for (NamingEnumeration ne = attrs.getAll(); ne.hasMore();) {
				Attribute attr = (Attribute) ne.next();// 得到下一个属性
				// 读取属性值
				for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
					String attrValue = e.next().toString();
					returnMap.put(attr.getID().toString(), attrValue);
				}
			}
		}
		return returnMap;
	}

}
